package com.aisino.encrypt.algorithm;

import com.aisino.encrypt.entity.Symmetric;
import com.aisino.encrypt.exception.AisinoException;

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;

/**
 * 对称加密算法
 * @author 蒋帆
 * @datetime 2021-08-25
 * @description DES( Data Encryption Standard ) 是一种数据加密标准
 * 使用一个 56 位的密钥以及附加的 8 位奇偶校验位，产生最大 64 位的分组大小。
 * 这是一个迭代的分组密码，使用称为 Feistel 的技术，其中将加密的文本块分成两半。
 * 使用子密钥对其中一半应用循环功能，然后将输出与另一半进行“异或”运算；
 * 接着交换这两半，这一过程会继续下去，但最后一个循环不交换。
 * DES 使用 16 个循环，使用异或，置换，代换，移位操作四种基本运算。 解密反之。
 */
public class SymmetricEncrypt {

    /**
     * 生成密钥
     * @param symmetric 加密类型（DES/DES3/AES）
     * @return 返回生成的密钥
     */
    public static String initKey(Symmetric symmetric) {
        String key = null;
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(symmetric.getAlgorithm());
            keyGenerator.init(symmetric.getKeySize());//初始化此密钥生成器,使其具有确定的密钥大小
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] bs = secretKey.getEncoded();
            key = Base64.getEncoder().encodeToString(bs);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        }
        return key;
    }

    /**
     * 根据传入的密钥位数生成密钥
     *
     * @param symmetric 加密类型（DES/DES3/AES）
     * @param keySize   密钥位数
     * @return 返回生成的密钥
     */
    public static String initKey(Symmetric symmetric, int keySize) {
        String key = null;
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(symmetric.getAlgorithm());
            keyGenerator.init(keySize);//初始化此密钥生成器,使其具有确定的密钥大小
            SecretKey secretKey = keyGenerator.generateKey();
            byte[] bs = secretKey.getEncoded();
            key = Base64.getEncoder().encodeToString(bs);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        }
        return key;
    }

    /**
     * 处理密钥
     * @param key 字符串密钥
     * @param symmetric 加密类型
     * @return 返回处理后算法需要的密钥
     */
    public static SecretKey init(String key, Symmetric symmetric) {
        SecretKey secretKey = null;
        try {
            switch (symmetric) {
                case DES:
                    DESKeySpec deskey = new DESKeySpec(Base64.getDecoder().decode(key));
                    // 创建一个密匙工厂，然后用它把DESKeySpec转换成一个SecretKey对象
                    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(symmetric.getAlgorithm());
                    secretKey = keyFactory.generateSecret(deskey);
                    break;
                case DES_EDE:
                    DESedeKeySpec desKeySpec = new DESedeKeySpec(Base64.getDecoder().decode(key));
                    // 创建一个密匙工厂，然后用它把DESKeySpec转换成一个SecretKey对象
                    SecretKeyFactory keyFactory2 = SecretKeyFactory.getInstance(symmetric.getAlgorithm());
                    secretKey = keyFactory2.generateSecret(desKeySpec);
                    break;
                case AES:
                    secretKey = new SecretKeySpec(Base64.getDecoder().decode(key), "AES");
                    break;
                default:
                    break;
            }
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        } catch (InvalidKeySpecException e) {
            throw new AisinoException(e);
        } catch (InvalidKeyException e) {
            throw new AisinoException(e);
        }
        return secretKey;
    }

    /**
     * 数据加密，算法（DES/DES3）
     *
     * @param content 要进行加密的数据
     * @param key 密钥
     * @param symmetric 算法类型
     * @return 加密后的数据
     */
    public static String encrypt(String content, String key, Symmetric symmetric) {
        String ciphertext = null;
        try {
            SecretKey secretKey = init(key, symmetric);
            // 加密对象
            Cipher cipher = Cipher.getInstance(symmetric.getAlgorithm() + "/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            // 加密，并把字节数组编码成字符串
            ciphertext = Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes()));
        } catch (NoSuchPaddingException e) {
            throw new AisinoException(e);
        } catch (IllegalBlockSizeException e) {
            throw new AisinoException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        } catch (BadPaddingException e) {
            throw new AisinoException(e);
        } catch (InvalidKeyException e) {
            throw new AisinoException(e);
        }
        return ciphertext;
    }

    /**
     * 数据解密，算法（DES/DES3）
     *
     * @param ciphertext 加密的数据
     * @param key 密钥
     * @param symmetric 加密类型
     * @return 解密后的数据
     */
    public static String decrypt(String ciphertext, String key, Symmetric symmetric) {
        String content = null;
        try {
            SecretKey secretKey = init(key, symmetric);
            // 解密对象
            Cipher cipher = Cipher.getInstance(symmetric.getAlgorithm() + "/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            // 把字符串解码为字节数组，并解密
            content = new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)));
        } catch (NoSuchPaddingException e) {
            throw new AisinoException(e);
        } catch (IllegalBlockSizeException e) {
            throw new AisinoException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new AisinoException(e);
        } catch (BadPaddingException e) {
            throw new AisinoException(e);
        } catch (InvalidKeyException e) {
            throw new AisinoException(e);
        }
        return content;
    }

}
